#ifndef __basic_iff_hh__
#define __basic_iff_hh__

#include <stdint.h>
#include <arpa/inet.h>
#include <fstream>
#include <list>

/* This macro rounds up to an even number. Taken from IFF.H, IFF-85
   description. */
#define ChunkWordAlign(size)   ((size + 1)& ~1)

typedef int32_t chunksize_t;

union ChunkID {
  char id[4];
  uint32_t as_int; //For quick comparison. Remember BE, LE!

  ChunkID() : as_int(0x20202020) {}
  ChunkID(const char *id_);

  bool operator<(ChunkID other) const;
  bool operator==(ChunkID other) const;
};
std::ostream &operator<<(std::ostream &out, ChunkID id);
extern const ChunkID chunkFORM;
extern const ChunkID chunkCAT;
extern const ChunkID chunkLIST;
extern const ChunkID chunkPROP;
extern const ChunkID chunkFiller;
extern const ChunkID chunkJUNK;


class WriterAbstract {
public:
  virtual std::ostream& operator()(std::ostream& o) const = 0;
  virtual ~WriterAbstract() {}
};

class WriterEmpty : public WriterAbstract {
protected:
  ChunkID ckID;

public:
  virtual std::ostream& operator()(std::ostream& o) const;
  WriterEmpty(ChunkID id) : ckID(id) {}
};

template<class T> class WriterObject : public WriterAbstract {
protected:
  ChunkID ckID;

  virtual std::ostream& write_data(std::ostream& o) const = 0;

public:
  const T& data;

  virtual std::ostream& operator()(std::ostream& o) const {
    o.write((const char*)&ckID, sizeof(ckID));
    int32_t l = htonl(sizeof(data));
    o.write((const char*)&l, sizeof(l));
    write_data(o);
    return o;
  }
  WriterObject(ChunkID id, const T& t_) : ckID(id), data(t_) {}
};

class WriterGroup : public WriterAbstract {
protected:
  ChunkID groupID;
  ChunkID subtype;

  virtual std::ostream& write_chunks(std::ostream& o) const;
public:
  std::list<const WriterAbstract*> chunks;

  WriterGroup(ChunkID id, ChunkID chunksubtype) : groupID(id), subtype(chunksubtype) {}

  std::ostream& operator()(std::ostream& o) const;
};

class WriterFORM : public WriterGroup {
public:
  WriterFORM(ChunkID chunksubtype) : WriterGroup(chunkFORM, chunksubtype) {}
};


class ReaderAbstract {
public:
  virtual ~ReaderAbstract() {}

  virtual std::istream& operator()(std::istream& o) = 0;
  virtual ChunkID id() const = 0;
  virtual chunksize_t size() const = 0;
  virtual off_t pos() const = 0;
};

class ReaderChunkHeader : public ReaderAbstract {
protected:
  chunksize_t ckSize;
  off_t ckPos;
  ChunkID ckID;

public:
  ReaderChunkHeader() : ckSize(-1), ckPos(-1) {}

  virtual std::istream& operator()(std::istream &strm);
  virtual ChunkID id() const;
  virtual chunksize_t size() const;
  virtual off_t pos() const;
  virtual off_t endpos() const;
  virtual void begin(std::istream &strm) const;
  virtual void end(std::istream &strm) const;
};

class ReaderChunkSkip : public ReaderChunkHeader {
public:
  virtual std::istream& operator()(std::istream &strm);
};

class ReaderGroupHeader : public ReaderChunkHeader {
protected:
  ChunkID ckSubtype;

public:
  virtual std::istream& operator()(std::istream &strm);
  virtual ChunkID subtype() const;
};

class ReaderSkipGroup : public ReaderGroupHeader {
public:
  virtual std::istream& operator()(std::istream &strm);
};


class ReaderGroupBase : public ReaderGroupHeader {
public:
  virtual std::istream& operator()(std::istream &strm);
  
  virtual bool handle(const ReaderGroupHeader&, const ReaderChunkHeader&, std::istream& ) = 0;
};

#endif
